package xj.graph2d.shapes;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;

import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.GraphNodeBase;
import xj.graph2d.SelfLoop;

public class OrthogonalSelfLoop 
  extends OrthogonalLineShape 
  implements SelfLoop, ShapeConstants {

  public OrthogonalSelfLoop(float x1, float y1, float x2, float y2,
			    float height, int direction) {
    this(x1, y1, x2, y2, height, direction, false);
  }

  public OrthogonalSelfLoop(float x1, float y1, float x2, float y2,
			    float height, int direction, boolean createComp) {
    super((direction == NORTH || direction == SOUTH), x1, y1, createComp);
    allowSelfLoop = true;
    this.height = height;
    this.direction = direction;
    float x3 = (x1 + x2) / 2;
    float y3 = (y1 + y2) / 2;
    if (direction == NORTH) {
      y3 = Math.min(y1, y2) - height;
    } else if (direction == SOUTH) {
      y3 = Math.max(y1, y2) + height;
    } else if (direction == EAST) {
      x3 = Math.max(x1, x2) + height;
    } else if (direction == WEST) {
      x3 = Math.min(x1, x2) - height;
    }
    addPoint(x3, y3);
    addPoint(x2, y2);
  }

  public OrthogonalSelfLoop(boolean createComp) {
    this(20, NORTH, createComp);
  }

  public OrthogonalSelfLoop(float height, int direction, boolean createComp) {
    super((direction == NORTH || direction == SOUTH), createComp);
    allowSelfLoop = true;
    this.height = height;
    this.direction = direction;
  }

  @Override
  public DrawShape makeInstance() {
    return new OrthogonalSelfLoop(height, direction, comp != null);
  }

  @Override
  public void copyTo(DrawShapeBase s) 
    throws CloneNotSupportedException {
    if (s != null) {
      super.copyTo(s);
      if (s instanceof OrthogonalSelfLoop) {
	OrthogonalSelfLoop r = (OrthogonalSelfLoop) s;
	r.height = height;
	r.direction = direction;
      }
    }
  }

  public boolean isOrthogonal() {
    return true; 
  }

  public boolean isOneSided() {
    return true; 
  }

  public int getDirection() {
    return direction;
  }

  public void setDirection(int direction) {
    this.direction = direction;
    // positionLabel();
    // adjustContent();
  }

  public float getHeight() {
    return height;
  }

  public void setHeight(float height) {
    this.height = height;
  }

  public float getWidth() {
    return 0;
  }

  public void setWidth(float width) {
  }

  public boolean isAdjustable() {
    return adjustable;
  }

  public void setAdjustable(boolean b) {
    adjustable = b;
  }

  public void adjustDirection(Rectangle2D bounds, 
			      float x1, float y1, float x2, float y2) {
    if (debug) {
      System.out.println("CubicCurveSelfLoop.adjustDirection()");
    }
    if (bounds != null) {
      float nx1 = (float) bounds.getX();
      float ny1 = (float) bounds.getY();
      float nx2 = nx1 + (float) bounds.getWidth();
      float ny2 = ny1 + (float) bounds.getHeight();

      float dx = Math.abs(x1 - x2);
      float dy = Math.abs(y1 - y2);
      float cx = (x2 + x1) / 2;
      float cy = (y2 + y1) / 2;
      if (dx >= dy) {
	// north or south
	if (Math.abs(cy - ny1) < Math.abs(cy - ny2)) {
	  direction = NORTH;
	} else {
	  direction = SOUTH;
	}
      } else {
	// east or west
	if (Math.abs(cx - nx1) < Math.abs(cx - nx2)) {
	  direction = WEST;
	} else {
	  direction = EAST;
	}
      }
      setStartVertical(direction == NORTH || direction == SOUTH);
    }
  }

  @Override
  public void adjustSelfLoop() {
    if (debug) {
      System.out.println("OrthogonalSelfLoop.adjustSelfLoop()");
    }
    float x1 = getX();
    float y1 = getY();
    float x2, y2;
    Point2D p = getEnd2();
    if (p != null) {
      x2 = (float) p.getX();
      y2 = (float) p.getY();
      float x3 = (x1 + x2) / 2;
      float y3 = (y1 + y2) / 2;
      if (direction == NORTH) {
	y3 = Math.min(y1, y2) - height;
      } else if (direction == SOUTH) {
	y3 = Math.max(y1, y2) + height;
      } else if (direction == EAST) {
	x3 = Math.max(x1, x2) + height;
      } else if (direction == WEST) {
	x3 = Math.min(x1, x2) - height;
      }
      setPoint(1, x3, y3);

      adapter.invalidateBounds();
      calculateBounds();

      if (debug) {
	System.out.println("OrthogonalSelfLoop.adjustSelfLoop() " + 
			   " x1=" + x1 + " y1=" + y1 + 
			   " x2=" + x2 + " y2=" + y2 + 
			   " x3=" + x3 + " y3=" + y3);
      }
    }
  }

  @Override
  public void adjustEnds(GraphNodeBase src, GraphNodeBase des, 
			 boolean adjustCtrPts) {
    if (debug) {
      System.out.println("OrthogonalSelfLoop.adjustEnds(boolean)");
    }
    super.adjustEnds(src, des, adjustCtrPts);
    if (!adjustable) { 
      adjustSelfLoop();
    }
  }

  @Override
  protected Map<String, Object> getXMLAttributes() {
    Map<String, Object> attrs = super.getXMLAttributes();
    if (attrs == null) {
      attrs = new HashMap<String, Object>();
    }
    attrs.put("height", height);
    attrs.put("direction", direction);
    return attrs;
  }

  @Override
  protected void processXMLAttributes(org.w3c.dom.Element node) {
    if (node != null) {
      super.processXMLAttributes(node);

      String s;
      s = node.getAttribute("height");
      try {
	height = Float.parseFloat(s);
      } catch (NumberFormatException ex) {
	height = 20;
      }
      s = node.getAttribute("direction");
      try { 
	direction = Integer.parseInt(s);
      } catch (NumberFormatException ex) {
	direction = NORTH;
      }
    }
  }

  protected float height;
  protected int direction;
  protected boolean adjustable = true;

  protected static final boolean debug = false;

}
